package kevin.framework.basic.controller;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.HandlerMapping;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

import kevin.framework.basic.common.annotation.ApiDescript;
import kevin.framework.basic.common.annotation.ApiParams;

@Controller
@RequestMapping(value = "/api")
public class ApiController {

    @Autowired
    WebApplicationContext appContext;

    Map<String, Object> controllerBeans = null;
    Map<String, HandlerMapping> allRequestMappings = null;
    List<ApiInfo> controllerList = new ArrayList<ApiInfo>();
    List<String> allControllerNames = new ArrayList<String>();

    @RequestMapping(value = "/page/{pageName}", method = RequestMethod.GET)
    public String page(@PathVariable("pageName") String pageName, HttpServletRequest request) throws Exception {
        return this.list(request);
    }

    @RequestMapping(value = "/list", method = RequestMethod.GET)
    public String list(HttpServletRequest request) {

        String ctxPathString = request.getContextPath();
        String filter = request.getParameter("c");
        if (controllerBeans == null) {
            controllerBeans = appContext.getBeansWithAnnotation(org.springframework.stereotype.Controller.class);
        }
        if (controllerList.size() == 0) {
            Set<Entry<String, Object>> sets = controllerBeans.entrySet();
            for (Entry<String, Object> entry : sets) {
                Object obj = entry.getValue();
                // String key = entry.getKey();
                Class clazz = obj.getClass();
                Class entityClass = null;
                Type type = clazz.getGenericSuperclass();
                if (type instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType)type;
                    entityClass = (Class)parameterizedType.getActualTypeArguments()[0];
                }

                Annotation pathAnnotion = clazz.getAnnotation(RequestMapping.class);
                if (pathAnnotion != null) {
                    String controller = ((RequestMapping)pathAnnotion).value()[0];
                    if (controller.equals("/api")) {
                        continue;
                    }
                    allControllerNames.add(controller);
                    ApiInfo actionList = new ApiInfo(controller);
                    controllerList.add(actionList);
                    Method[] methods = clazz.getDeclaredMethods();
                    List<ActionInfo> actions = getMethodDetails(methods, controller, entityClass, ctxPathString);
                    for (ActionInfo act : actions) {
                        actionList.putAction(act);
                    }
                    // 获取基类上的 方法
                    while (clazz != Object.class) {
                        clazz = clazz.getSuperclass();
                        methods = clazz.getDeclaredMethods();
                        actions = getMethodDetails(methods, controller, entityClass, ctxPathString);
                        for (ActionInfo act : actions) {
                            actionList.putAction(act);
                        }
                    }
                }
            }
        }
        List<ApiInfo> retList;
        if (StringUtils.isNotEmpty(filter)) {
            retList = new ArrayList<ApiInfo>();
            for (ApiInfo p : controllerList) {
                if (p.controller.equals(filter)) {
                    retList.add(p);
                }
            }
        } else {
            retList = controllerList;
        }

        String json = JSONObject.toJSONString(retList, SerializerFeature.DisableCircularReferenceDetect);
        request.setAttribute("apilist", json);

        request.setAttribute("controllers", JSONObject.toJSONString(allControllerNames));
        return "base/api/index";

    }

    private List<ActionInfo> getMethodDetails(Method[] methods, String controller, Class entityClass,
        String ctxPathString) {
        List<ActionInfo> list = new ArrayList<ActionInfo>();
        for (Method f : methods) {
            if (f.getName().equals("page") || f.getName().equals("checkFileCreate")) {
                continue;
            }
            RequestMapping reqMap = f.getAnnotation(RequestMapping.class);
            if (reqMap != null) {
                String action = reqMap.value()[0];
                if (StringUtils.isNotEmpty(action)) {
                    ActionInfo resMap = new ActionInfo();

                    String url = controller + "/" + action;
                    if (!ctxPathString.equals("/")) {
                        url = ctxPathString + url;
                    }
                    resMap.url = url.replace("//", "/");

                    ApiDescript apiDesc = f.getAnnotation(ApiDescript.class);
                    ApiParams apiPrs = f.getAnnotation(ApiParams.class);

                    resMap.params = "无参数说明";
                    resMap.descript = "无接口描述";
                    if (apiDesc != null) {
                        resMap.descript = apiDesc.value();
                    }
                    if (apiPrs != null) {
                        resMap.params = apiPrs.value();
                    } else if (entityClass != null) {
                        String tmpAct = action.replaceAll("/", "");
                        Field[] entityFields = entityClass.getDeclaredFields();
                        StringBuffer buffer = new StringBuffer();
                        buffer.append("{");
                        for (int i = 0, len = entityFields.length; i < len; ++i) {
                            Field filed = entityFields[i];
                            String name = filed.getName();
                            if (name.equals("serialVersionUID")) {

                                continue;
                            }
                            boolean appendDot = false;
                            if (tmpAct.equals("add")) {
                                if (!name.equals("id")) {
                                    appendDot = true;
                                    buffer.append("'" + name + "' : ''");
                                }
                            } else if (tmpAct.equals("update")) {
                                appendDot = true;
                                buffer.append("'" + name + "' : ''");
                            }
                            if (i < len - 1 && appendDot) {
                                buffer.append(" , ");
                            }
                        }
                        buffer.append("}");
                        resMap.params = buffer.toString().replaceAll("\\{,\\{", "\\{\\{").replaceAll("\\{,'", "\\{'");
                    }
                    list.add(resMap);
                }
            }
        }
        return list;
    }

    class ApiInfo {
        String controller;
        List<ActionInfo> actions;

        public ApiInfo(String controller) {
            this.controller = controller;
            actions = new ArrayList<ActionInfo>();
        }

        public void putAction(ActionInfo info) {
            actions.add(info);
        }

        public String getController() {
            return controller;
        }

        public List<ActionInfo> getActions() {
            return actions;
        }

    }

    class ActionInfo {
        String descript;
        String url;
        String params;

        public String getDescript() {
            return descript;
        }

        public void setDescript(String descript) {
            this.descript = descript;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getParams() {
            return params;
        }

        public void setParams(String params) {
            this.params = params;
        }

    }

}
